#include <tsin/library/SCDLInterpreter.hpp>

#include <scdl/parser/core.hpp>
#include <scdl/parser/StringMap.hpp>

#include <tsin/library/GameEvent.hpp>
#include <tsin/library/BulletEventFiller.hpp>

#include <tsin/library/TouhouSoloViewport.hpp>
#include <tsin/library/TouhouPlayerFactory.hpp>
#include <tsin/library/BulletCirclePlacer.hpp>
#include <tsin/library/BulletCircleDirector.hpp>
#include <tsin/library/BulletLineRouter.hpp>
#include <tsin/library/BulletFadingDresser.hpp>
#include <tsin/library/BuiltinImages.hpp>

#include <tsin/library/BulletDieOutEvent.hpp>

#include <tsin/runtime/GameRuntime.hpp>
#include <tsin/runtime/BulletRuntime.hpp>
#include <tsin/util/Logger.hpp>

#include <scdl/parser/Symbol.hpp>

#include <cstring>
#include <iostream>

using namespace tsin;
using namespace scdl;
using scdl::StringMap;

#include <ctype.h>
#define _USE_MATH_DEFINES
#include <cmath>

class LIBRARY_DLLEXPORT ListEventFiller : public BulletEventFiller {
public:
        ListEventFiller() {}

        virtual ~ListEventFiller() {}

        virtual DynamicObject *copy()
        {
                return DynamicObject::copy(new ListEventFiller);
        }

        virtual void reinit() {}

        virtual void reset() {}

        virtual DynamicEvent postTick()
        {
                return EVENT_NONE;
        }

        virtual void fill(BulletRuntime *bullet)
        {
                for (int i = 0; i < nrArguments; i++) {
                        DynamicObject *arg = arguments[i];
                        DynamicObject *obj = arg->copy();
                        BulletEvent *e = static_cast<BulletEvent *>(obj);
                        bullet->addBulletEvent(e);

                        //arg->postTick();
                }
        }
};

extern "C" LIBRARY_DLLEXPORT DynamicObject *bindListEventFiller()
{
        return new ListEventFiller;
}

class LIBRARY_DLLEXPORT SCDLEventAdapter : public DynamicObject {
        DynamicObject *trigger, *generator;
public:
        SCDLEventAdapter() : trigger(0), generator(0)
        {}

        virtual void reinit()
        {
                 DynamicObject *arg1 = arguments[0];
                 DynamicObject *arg2 = arguments[1];

                 switch(nrArguments) {
                 case 2:
                         if (strcmp(desp, "PP") == 0) {
                                 trigger = arg1;
                                 generator = arg2;
                         }
                         break;
                 }
        }

        virtual DynamicEvent postTick(void *, void*)
        {
                if (trigger->postTick() & EVENT_TRIGGER)
                        generator->postTick();
                return EVENT_NONE;
        }

        virtual char getDescriptor()
        {
                return 'P';
        }
};

extern "C" LIBRARY_DLLEXPORT DynamicObject *bindGameEvent()
{
        return new SCDLEventAdapter;
}

class LIBRARY_DLLEXPORT SCDLGameEvent : public GameEvent {
        DynamicObject *obj;

public:
        SCDLGameEvent(Symbol *sym)
        {
                obj = DynamicObject::interpret(sym);
        }

        void init(GameRuntime &game)
        {
        }

        bool trigger(GameRuntime &game)
        {
                return true;
        }

        GameEvent *handle(GameRuntime &game)
        {
                obj->postTick();
                return this;
        }
};

SCDLInterpreter *SCDLInterpreter::openSCDL(const char *file)
{
        if (!DynamicObject::openLibrary("library.so"))
                return 0;
        if (scdl_parse(static_cast<char *>((void*)file)))
                return 0;
        return new SCDLInterpreter(get_symbol_table());
}

SCDLInterpreter::SCDLInterpreter(StringMap *symbols)
        : symbols(*symbols)
{
        name = "SCDL-test";

        front.init(library::thSoloMiddleViewport, r640x480);
        frontImagePath = RES_PREFIX "front/front128.png";
}

SCDLInterpreter::~SCDLInterpreter()
{
}

int SCDLInterpreter::getEvents(GameEvent **events)
{
        Identifier *playerId = static_cast<Identifier *>(getDef("player"));
        Character c;
        if (playerId) {
                c = static_cast<Character>(playerId->getInductiveCtorValue());
        } else
                c = HAKUREI_REIMU;
        Player *player = TouhouPlayerFactory::generatePlayer(c);
        currentGame->players->insert(player);

        Symbol *event = getDef("game");
        events[0] = new SCDLGameEvent(event);
        return 1;
}


Symbol *SCDLInterpreter::getDef(const char *name)
{
        Symbol *list = symbols[name];
        if (list == 0)
                return 0;

        Symbol *def = static_cast<IdentifierList *>(list)->getElement(0);
        if (def == 0)
                return 0;

        return static_cast<Identifier *>(def)->getDef();
}
